#pragma once

#include <math.h>
#include <list>
#include <string.h>
using namespace std;
static const float PI = 3.1415926;
static const float NORMALEN = 0.2;
class Shape
{
public:
    string name; 
	bool bNormal;
public:
	Shape(void);
	virtual ~Shape(void);	
	virtual void drawShape() = 0;
	virtual void incrementSlice()=0;
	virtual void decrementSlice()=0;
	virtual void incrementStack()=0;
	virtual void decrementStack()=0;
};

typedef struct M3DPoint {
	float point[4];
	M3DPoint(float ax = 0, float ay=0, float az=0){
		point[0] = ax;
		point[1] = ay;
		point[2] = az;
		point[3] = 1;
	}

	M3DPoint(const M3DPoint &other) {
			point[0] = other.point[0];
			point[1] = other.point[1];
			point[2] = other.point[2];
			point[3] = other.point[3];
	}

	M3DPoint translate(float dx, float dy, float dz) {
		M3DPoint rt(*this);
		rt.point[0] += dx;
		rt.point[1] += dy;
		rt.point[2] += dz;
		return rt;
	}

	M3DPoint rotateX(float theta) {	//rotate around x axis by theta, in radian
		M3DPoint rt(*this);
		rt.point[1] = cos(theta)*(this->point[1]) - sin(theta)*(this->point[2]);
		rt.point[2] = sin(theta)*(this->point[1]) + cos(theta)*(this->point[2]);
		return rt;
	}

	M3DPoint rotateY(float theta) {	//rotate around y axis by theta, in radian
		M3DPoint rt(*this);
		rt.point[0] = cos(theta)*(this->point[0]) + sin(theta)*(this->point[2]);
		rt.point[2] = -sin(theta)*(this->point[0]) + cos(theta)*(this->point[2]);
		return rt;
	}

	M3DPoint rotateZ(float theta) {	//rotate around z axis by theta, in radian
		M3DPoint rt(*this);
		rt.point[0] = cos(theta)*(this->point[0]) - sin(theta)*(this->point[1]);
		rt.point[1] = sin(theta)*(this->point[0]) + cos(theta)*(this->point[1]);
		return rt;
	}

	//assuming, sx, sy, sz is not 0
	M3DPoint scaling(float sx, float sy, float sz) {
		M3DPoint rt(*this);
		rt.point[0] *= sx;
		rt.point[1] *= sy;
		rt.point[2] *= sz;
		return rt;
	}

	M3DPoint operator +(const M3DPoint &o1) {
		M3DPoint v(*this);
		v.point[0] += o1.point[0];
		v.point[1] += o1.point[1];
		v.point[2] += o1.point[2];
		return v;
	}

	M3DPoint &operator=(const M3DPoint &p1) {
		this->point[0] = p1.point[0];
		this->point[1] = p1.point[1];
		this->point[2] = p1.point[2];
		this->point[3] = p1.point[3];
		return *this;
	}

	float length() const{
		return sqrt(pow(point[0],2)+pow(point[1],2)+pow(point[2],2));
	}

	M3DPoint CrossProduct(const M3DPoint &p1) {
		M3DPoint v(*this);
		v.point[0] = this->point[1]*p1.point[2] - this->point[2]*p1.point[1];
		v.point[1] = this->point[2]*p1.point[0] - this->point[0]*p1.point[2];
		v.point[2] = this->point[0]*p1.point[1] - this->point[1]*p1.point[0];
		return v;
	}

	M3DPoint normailize() {
		float f = 1/length();
		return scaling(f,f,f);
	}

	M3DPoint operator *(float x) {
		return scaling(x,x,x);
	}

} MVertex;

M3DPoint getVertex(const M3DPoint &p1, const M3DPoint &p2);
void calculateNormal(MVertex *p, const M3DPoint &v, const M3DPoint &t, 
							const M3DPoint &b, const M3DPoint &l, const M3DPoint &r);